package com.jbp.service;

import com.alibaba.fastjson.JSON;
import com.jbp.exception.RRException;
import com.jbp.model.*;
import com.jbp.redis.RedisConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.FileInputStream;
import java.io.FileWriter;
import java.math.BigDecimal;
import java.util.*;

/**
 * 金牛 100块奖池 1：10
 */
@Service
public class LuckyUtil32 {
    @Autowired
    private RedisConfigService redisConfigService;

    @Autowired
    private PropertyService propertyService;

    @Autowired
    private UserService userService;

    //每次抽消耗奖金币
    public static final int price = 1000;
    @Autowired
    private LuckPoolUserService luckPoolUserService;
    @Autowired
    private LuckPoolRoomService luckPoolRoomService;

    @Autowired
    private LuckyUtil luckyUtil;
    /**
     * patch 补礼物  0表示不补
     * */
    public List<Gift> luck(Integer userId, int times, List<GiftLot> list, int patch, Integer roomId) {

        //系统总爆率
        double winRate;
        double winRateA = Double.parseDouble(propertyService.getV("winRateA"));
        double winRateB = Double.parseDouble(propertyService.getV("winRateB"));
        double winRateC = Double.parseDouble(propertyService.getV("winRateC"));
        double winRateD = Double.parseDouble(propertyService.getV("winRateD"));

        //个人最高爆率
        double personRateHigh = Double.parseDouble(propertyService.getV("luck_high_usr"));
        //奖池礼物数量
        int poolSize = Integer.parseInt(propertyService.getV("pool_size2"));
        int minPoolSize = Integer.parseInt(propertyService.getV("min_pool_size2"));
        int maxGift = Integer.parseInt(propertyService.getV("maxGift"));
        double jcBeishu = Double.parseDouble(propertyService.getV("pool_times"));

        String keyUpay = "haoya:luck:"+price+":upay:"+userId;
        String keyUincmoe = "haoya:luck:"+price+":uincome:"+userId;
        String keyUTotalPay = "haoya:luck:"+price+":utotalpay:"+userId;
        String keyUTotalIncome = "haoya:luck:"+price+":utotalincome:"+userId;
        String keyUmap = "haoya:luck:"+price+":umap:"+userId;

        int pay = redisConfigService.getInt(keyUpay);
        int income = redisConfigService.getInt(keyUincmoe);
        int totalPay = redisConfigService.getInt(keyUTotalPay);
        int totalIncome = redisConfigService.getInt(keyUTotalIncome);

        double userBaseRate = luckyUtil.getBaseRate(roomId, userId, totalPay);

        //判断奖池
        String poolName;
        int type;
        double basePoolRate = 1;
        if (totalPay+times*price<10000) {
            poolName = "A";
            type = 1;
            winRate = winRateA;
        } else if (totalPay+times*price<100000) {
            poolName = "B";
            type = 2;
            winRate = winRateB;
        }  else if (totalPay+times*price<500000) {
            poolName = "C";
            type = 3;
            winRate = winRateC;
        }  else {
            poolName = "D";
            type = 4;
            winRate = winRateD;
        }

        LuckPoolRoom luckPoolRoom = luckPoolRoomService.getByRoomId(roomId);
        LuckPoolUser luckPoolUser = luckPoolUserService.getByUserId(userId);
        String poolPrex = "";
        if (luckPoolUser!=null) {
            poolPrex = "u:"+userId+":";
            winRate *= luckPoolUser.getRate().doubleValue() / 1.17d;
        } else if (luckPoolRoom!=null) {
            poolPrex = "r:"+roomId+":";
            winRate *= luckPoolRoom.getRate().doubleValue() / 1.17d;
        }

        double patchRate = winRate<1.17 ? 1.17-winRate : 0;

        Node nodeTree = getNodeTree(type);

        String keyAllincome= "haoya:luck:"+price+":allincome:"+poolPrex+poolName;
        String keyAllpay = "haoya:luck:"+price+":allpay:"+poolPrex+poolName;
        String keyPool = "haoya:luck:"+price+":pool:"+poolPrex+poolName;
        String keyGiftCount = "haoya:luck:"+price+":giftcount:"+poolPrex+poolName;
        String keyPatchPool = "haoya:luck:patchpool";
        String keyPoolMap = "haoya:luck:"+price+":pollmap:"+poolPrex+type;

        int allIncome = redisConfigService.getInt(keyAllincome);
        int allPay = redisConfigService.getInt(keyAllpay);
        int pool = redisConfigService.getInt(keyPool);
        int giftCount = redisConfigService.getInt(keyGiftCount);
        int patchPool = redisConfigService.getInt(keyPatchPool);

        //用户积分树
        String ujson = (String) redisConfigService.get(keyUmap);
        Map<String, Integer> userNMap = (Map<String, Integer>) JSON.parse(ujson);
        if (userNMap==null) {
            userNMap = new LinkedHashMap<>();
        }
        //奖池积分树
        String pjson = (String) redisConfigService.get(keyPoolMap);
        Map<String, Integer> allNMap = (Map<String, Integer>) JSON.parse(pjson);
        if (allNMap==null) {
            allNMap = new LinkedHashMap<>();
        }

        //生成奖池
        if (giftCount<=minPoolSize) {
            giftCount += poolSize;
            pool += poolSize * winRate * price;
            System.out.println("生成奖池："+pool);
            patchPool += poolSize * patchRate * price;
        }

        //当前抽奖十连或百连
        int currPay = 0, currIncome=0;
        List<Gift> result = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            giftCount --;
            pay += price;
            totalPay += price;
            allPay += price;
            currPay += price;

            //用户最大奖限制
            int biggest = (int) (pool/jcBeishu);
            int min = 1;
            int luck;
            if (patch==0) {
                biggest = Math.min(biggest, maxGift);
                biggest = Math.max(biggest, 4444);
                biggest = Math.min(biggest, 131400);

                int lose = pay - income;
                if (totalPay <= 500000 && totalPay > 300000 && lose>0) {
                    min = lose;
                }

                // 十连保底
                if (times==10) {
                    int currMin = 5000;//十连B奖池保底
                    if (type!=2) { //其他奖池
                        currMin = 3000;
                    }
                    if (currPay==10000 && currIncome<currMin) {
                        min = currMin - currIncome;
                        biggest = 6666;
                    }
                } else if (times==100) {
                    int currMin = 80000;
                    if (currPay==100000 && currIncome<currMin) {
                        min = currMin - currIncome;
//                        biggest = 66666;
                    }
                }
                min = Math.min(min, biggest);

                double poolRate = getPoolRate(pool, giftCount, winRate);
                luck = nodeLuck(nodeTree, poolRate, min, biggest, userNMap, allNMap, basePoolRate*userBaseRate);
            } else {
                luck = patch;
//                patch(nodeTree,luck, userNMap, allNMap);
            }

            //将奖品转化为礼物
            Gift gift = null;
            for (GiftLot giftLot : list) {
                if (giftLot.getGift().getCoin() == luck) {
                    gift = giftLot.getGift();
                    break;
                }
            }

            if (gift==null) {
                System.out.println("抽奖异常");
                throw new RRException("网络错误");
            }

            //新号补不计入爆率
            income += gift.getCoin();
            totalIncome += gift.getCoin();
            allIncome += gift.getCoin();
            currIncome += gift.getCoin();
            if (patch==0) {
                pool -= gift.getCoin();
            }
            result.add(gift);
        }

        redisConfigService.set(keyAllincome, allIncome);
        redisConfigService.set(keyAllpay, allPay);
        redisConfigService.set(keyPool, pool);
        redisConfigService.set(keyPatchPool, patchPool);
        redisConfigService.set(keyGiftCount, giftCount);
        redisConfigService.set(keyUpay, pay);
        redisConfigService.set(keyUincmoe, income);
        redisConfigService.set(keyUTotalPay, totalPay);
        redisConfigService.set(keyUTotalIncome, totalIncome);
        redisConfigService.set(keyUmap, JSON.toJSONString(userNMap));
        redisConfigService.set(keyPoolMap, JSON.toJSONString(allNMap));
        return result;
    }

    public void addPatch(int coin) {
        Integer r_patch_pool = (Integer) redisConfigService.get("haoya:luck:patchpool");
        int patchPool = r_patch_pool!=null ? r_patch_pool : 0;
        patchPool += coin;
        redisConfigService.set("haoya:luck:patchpool", patchPool);
    }

    private static void printConfig(Map<Integer, Integer> map) {
        //总价值
        long totalCoin = map.entrySet().stream().mapToInt(e -> e.getKey()*e.getValue()).sum();
        //总数量
        long totalSize = map.values().stream().mapToInt(v->v).sum();
        double zbl = BigDecimal.valueOf(totalCoin).divide(BigDecimal.valueOf(totalSize*price), 6, BigDecimal.ROUND_HALF_UP).doubleValue();

        map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach(e->{
            double bl = BigDecimal.valueOf(e.getValue() * 100).divide(BigDecimal.valueOf(totalSize), 6, BigDecimal.ROUND_HALF_UP).doubleValue();
            double zb = BigDecimal.valueOf(e.getValue() * e.getKey() * 100).divide(BigDecimal.valueOf(totalCoin), 6, BigDecimal.ROUND_HALF_UP).doubleValue();

            System.out.println(e.getKey()+"钻  "+ e.getValue()+"个   "+" 爆率:"+ bl +"    占比:"+zb);
        });
        System.out.println("总数量："+totalSize+"  总价值:"+totalCoin+"  总爆率："+zbl);
    }

    //清空个人记录
    public void clearUserHistory(){
        System.out.println("清个人奖池数据");
        //个人投入
        redisConfigService.removePattern("haoya:luck:upay:*");
        //个人产出
        redisConfigService.removePattern("haoya:luck:uincome:*");
        //个人总投入
        redisConfigService.removePattern("haoya:luck:utotalpay:*");
        //个人总产出
        redisConfigService.removePattern("haoya:luck:utotalincome:*");
        System.out.println("清奖个人池数据完成");
    }

    public void  clearPool(){
        System.out.println("-----清空奖池-----");
        redisConfigService.set("haoya:luck:allincome:A", 0);
        redisConfigService.set("haoya:luck:allpay:A", 0);
        redisConfigService.set("haoya:luck:pool:A", 0);
        redisConfigService.set("haoya:luck:giftcount:A", 0);
        redisConfigService.set("haoya:luck:patchpool", 0);

        redisConfigService.set("haoya:luck:allincome:B", 0);
        redisConfigService.set("haoya:luck:allpay:B", 0);
        redisConfigService.set("haoya:luck:pool:B:0", 0);
        redisConfigService.set("haoya:luck:giftcount:B", 0);

        redisConfigService.set("haoya:luck:allincome:C", 0);
        redisConfigService.set("haoya:luck:allpay:C", 0);
        redisConfigService.set("haoya:luck:pool:C", 0);
        redisConfigService.set("haoya:luck:giftcount:C", 0);

        redisConfigService.set("haoya:luck:allincome:D", 0);
        redisConfigService.set("haoya:luck:allpay:D", 0);
        redisConfigService.set("haoya:luck:pool:D", 0);
        redisConfigService.set("haoya:luck:giftcount:D", 0);
    }

    Pool poolA = new Pool();
    Pool poolB = new Pool();
    Pool poolC = new Pool();
    Pool poolD = new Pool();
    @SuppressWarnings("AliControlFlowStatementWithoutBraces")
    void luckTest(UserLuck userLuck, Map <Integer, Integer> out) {
        //总爆率
        double winRate;

        Pool pl;
        Node nodeTree;
        int type;
        if (userLuck.totalPay<10000) {
            pl = poolA;
            type =1;
            winRate = 1.15;
//            if (pl.giftCount>0 && pl.pool>0) {
//                System.out.println(" 剩余礼物:"+pl.giftCount+"    水池:"+pl.pool +"   爆率："+BigDecimal.valueOf(pl.pool).divide(BigDecimal.valueOf(pl.giftCount*20), 4, BigDecimal.ROUND_HALF_UP).doubleValue());
//            }
        } else if (userLuck.totalPay<100000) {
            pl = poolB;
            type = 2;
            winRate = 1.145;

        } else if (userLuck.totalPay<500000) {
            pl = poolC;
            type = 3;
            winRate = 1.145;

        } else {
            pl = poolD;
            type = 4;
            winRate = 1.135;
//            if (pl.giftCount>0 && pl.pool>0) {
//                System.out.println(" 剩余礼物:"+pl.giftCount+"    水池:"+pl.pool +"   爆率："+BigDecimal.valueOf(pl.pool).divide(BigDecimal.valueOf(pl.giftCount*20), 4, BigDecimal.ROUND_HALF_UP).doubleValue());
//            }
        }
//        pl = poolD;
//        type = 4;
//        winRate = 1.15;
        nodeTree = getNodeTree(type);

        //生成奖池
        if (pl.giftCount<500) {
            pl.giftCount += pl.giftSize;
            pl.pool += pl.giftSize * winRate * price;
        }

        pl.giftCount --;
        userLuck.pay += price;
        userLuck.totalPay += price;
        userLuck.currPay += price;
        pl.allPay += price;


        //用户最大奖限制
        int biggest = pl.pool/4;
        int min = 1;

        biggest = Math.min(biggest, 131400);
        biggest = Math.max(biggest, 4444);

        /*if(biggest<300000) {
            System.out.println("biggest:"+biggest);
        }*/
        int lose = (int) (userLuck.pay-userLuck.income);
        if (userLuck.pay <= 500000 && userLuck.pay > 300000 && lose>0) {
            min = lose;
        }

        /*int lose = (int) (userLuck.pay*winRate-userLuck.income);
        if (userLuck.pay<=1000000 && userLuck.pay>999000 && userLuck.pay-userLuck.income-100000>1) {
            min = userLuck.pay - userLuck.income-100000;
        }  else if (userLuck.pay<1000000 && lose>200000 && biggest>=52000) {
            min = 10000;
        }  else if (userLuck.totalPay<100000 && lose>50000 && biggest>=52000) {
            min = 5000;
            biggest = 33440;
        } else if (userLuck.totalPay<60000 && lose>28000 && biggest>=52000) {
            min = 5000;
            biggest = 18888;
        }*/ /*else if (userLuck.totalPay<20000 && lose>7000 && biggest>=52000) {
            min = 500;
            biggest = 10000;
        } else if (userLuck.totalPay<10000 && lose>3000 && biggest>=52000) {
            min = 100;
            biggest = 5200;
        }*/


        if (userLuck.totalPay==10000 && userLuck.totalIncome<3000) {
            min = 3000-userLuck.totalIncome;
            biggest = 6666;
//            System.out.println(min);
        }

        int currMin = 80000;
        if (userLuck.totalPay==100000 && userLuck.totalIncome<currMin) {
            min = currMin - userLuck.totalIncome;
            biggest = 66666;
        }

        min = Math.min(min, biggest);

        double poolRate = getPoolRate(pl.pool, pl.giftCount, winRate);
        Map<String, Integer> nMap = getUserNMap(userLuck.id);
        Map<String, Integer> allMap = getPoolNMap(type);
        int luck = nodeLuck(nodeTree, poolRate, min, biggest, nMap, allMap, 1);

        userLuck.income += luck;
        userLuck.totalIncome += luck;
        userLuck.currIncome += luck;
        pl.allIncome += luck;
        pl.pool -= luck;

        if (out.containsKey(luck)) {
            out.put(luck, out.get(luck)+1);
        } else {
            out.put(luck, 1);
        }
    }

    static double getPoolRate(int pool, int giftSize, double winRate ) {
        int out = (int) (pool - giftSize*winRate*price);
        double rate = 1 + 0.1 * (out / 10000);
        rate = Math.max(0.5, rate);
        rate = Math.min(1.7, rate);
        return rate;
    }
    /**
     * A奖池 投入1w钻以内
     */
    private static Map <Integer, Integer> getGiftCoinfigA() {
        Map<Integer, Integer> map = new LinkedHashMap<>();
        map.put(188, 280);
        map.put(521, 550);
        map.put(1111, 120);
        map.put(3333, 60);
        map.put(6666, 38);
        map.put(9999, 12);
        map.put(13140, 6);
        map.put(18888, 3);
        map.put(33440, 1);
        return map;
    }
    /**
     * B奖池 投入10w钻以内
     */
    private static Map <Integer, Integer> getGiftCoinfigB() {
        Map<Integer, Integer> map = new LinkedHashMap<>();
        map.put(188, 680);
        map.put(521, 1100);
        map.put(1111, 200);
        map.put(3333, 100);
        map.put(6666, 66);
        map.put(9999, 18);
        map.put(13140, 18);
        map.put(18888, 12);
        map.put(33440, 5);
        map.put(52000, 1);
        return map;
    }

    /**
     * C奖池 投入50w钻以下
     */
    private static Map <Integer, Integer> getGiftCoinfigC() {
        Map<Integer, Integer> map = new LinkedHashMap<>();
        map.put(188, 680);
        map.put(521, 1100);
        map.put(1111, 180);
        map.put(3333, 90);
        map.put(6666, 60);
        map.put(9999, 14);
        map.put(13140, 14);
        map.put(18888, 10);
        map.put(33440, 5);
        map.put(52000, 2);
        map.put(77777, 1);
        return map;
    }

    /**
     * D奖池 投入50w钻以上
     */
    private static Map <Integer, Integer> getGiftCoinfigD() {
        Map<Integer, Integer> map = new LinkedHashMap<>();
        map.put(188, 900);
        map.put(521, 1100);
        map.put(1111, 160);
        map.put(3333, 80);
        map.put(6666, 60);
        map.put(9999, 10);
        map.put(13140, 12);
        map.put(18888, 10);
        map.put(33440, 8);
        map.put(52000, 3);
        map.put(77777, 2);
        map.put(99999, 1);
        return map;
    }

    private static void tjUser(List<UserLuck> userLucks) {
        //总人数
        int totalCount = userLucks.size();
        //盈利人数
        double winRate = 0.85d;
        int winCount = (int) userLucks.stream().filter(user-> user.totalIncome* winRate-user.totalPay >0).count();
        //总盈利
        long sumWin = userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay > 0).mapToLong(user -> (int) (user.totalIncome* winRate-user.totalPay)).sum();
        // 平均盈利
        long avgWin = sumWin/winCount;
        //最大盈利
        int maxWin = userLucks.stream().filter(user -> 1d*user.totalIncome / user.totalPay > winRate && user.totalIncome* winRate-user.totalPay > 0).mapToInt(user -> (int) (user.totalIncome* winRate-user.totalPay)).max().getAsInt();
        //最大爆率
        double maxRate = userLucks.stream().mapToDouble(user -> 1d*user.totalIncome*1d / user.totalPay).max().getAsDouble();

        //亏损人数
        int loseCount = totalCount - winCount;
        //总亏损
        long sumLose = userLucks.stream().filter(user -> (1d*user.totalIncome / user.totalPay <= winRate) || (user.totalIncome* winRate-user.totalPay < 0)).mapToLong(user -> (int) (user.totalIncome* winRate-user.totalPay)).sum();
        //平均亏损
        long avgLose = loseCount==0 ? loseCount : sumLose/loseCount;
        //最大亏损
        int maxLose = userLucks.stream().filter(user -> (1d*user.totalIncome / user.totalPay <= winRate) || (user.totalIncome* winRate-user.totalPay < 0)).mapToInt(user -> (int) (user.totalIncome* winRate-user.totalPay)).min().getAsInt();
        double minRate = userLucks.stream().mapToDouble(user -> 1d*user.totalIncome*1d / user.totalPay).min().getAsDouble();

        System.out.println("抽奖总人数："+totalCount);
        System.out.println("盈利人数:"+winCount+"  平均盈利:"+avgWin+" 最大盈利："+maxWin + " 最大爆率:"+maxRate);
        System.out.println("亏损人数:"+loseCount+"  平均亏损:"+avgLose+" 最大亏损："+maxLose + " 最小爆率:"+minRate);

        System.out.println("---用户亏损统计----");
        System.out.println("亏损大于10w钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay < -100000).count());
        System.out.println("亏损5w-10w钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= -100000 && user.totalIncome* winRate-user.totalPay < -50000).count());
        System.out.println("亏损<5w钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= -50000&& user.totalIncome* winRate-user.totalPay < 0).count());
        System.out.println("---用户盈利统计----");
        System.out.println("200<盈利<1000钻: "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 200 && user.totalIncome* winRate-user.totalPay < 1000).count());
        System.out.println("1000<盈利<2000钻: "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 1000 && user.totalIncome* winRate-user.totalPay < 2000).count());
        System.out.println("2000<盈利<5000钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 2000 && user.totalIncome* winRate-user.totalPay < 5000).count());
        System.out.println("5000<盈利<10000钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 5000 && user.totalIncome* winRate-user.totalPay < 10000).count());
        System.out.println("10000<盈利<100000钻： "+userLucks.stream().filter(user -> user.totalIncome* winRate-user.totalPay >= 10000 && user.totalIncome* winRate-user.totalPay < 100000).count());

        System.out.println("---用户爆率统计----");
        System.out.println("爆率<0.7： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 0.7d).count());
        System.out.println("0.7<爆率<0.8： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 0.8d && user.totalIncome*1d / user.totalPay >= 0.7d).count());
        System.out.println("0.8<爆率<0.9： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 0.9d && user.totalIncome*1d / user.totalPay >= 0.8d).count());
        System.out.println("0.9<爆率<1： "+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1d && user.totalIncome*1d / user.totalPay >= 0.9d).count());
        System.out.println("1<爆率<1.1："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.1d && user.totalIncome*1d / user.totalPay >= 1d).count());
        System.out.println("1.1<爆率<1.15："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.15d && user.totalIncome*1d / user.totalPay >= 1.1d).count());
        System.out.println("1.15<爆率<1.2："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.2d && user.totalIncome*1d / user.totalPay >= 1.15d).count());
        System.out.println("1.2<爆率<1.3："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.3d && user.totalIncome*1d / user.totalPay >= 1.2d).count());
        System.out.println("1.3<爆率<1.5："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.5d && user.totalIncome*1d / user.totalPay >= 1.3d).count());
        System.out.println("1.5<爆率<1.8："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 1.8d && user.totalIncome*1d / user.totalPay >= 1.5d).count());
        System.out.println("1.8<爆率<2.5："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 2.5d && user.totalIncome*1d / user.totalPay >= 1.8d).count());
        System.out.println("2.5<爆率<5："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay < 5d && user.totalIncome*1d / user.totalPay >= 2.5d).count());
        System.out.println("5<爆率："+userLucks.stream().filter(user -> user.totalIncome*1d / user.totalPay >= 5d).count());
    }

    /**
     1. 存储用户每个礼物对应的N值 默认为1 未抽中+1 抽中置为1
     2. prd算法 P(实际概率)=C(基础爆率)*N(抽奖次数)
     */
    private static Map<Integer, Map<Integer, Integer>> userNMap = new HashMap<>();

    /**
     * 获取用户真实爆率表
     * 实际礼物爆率为 礼物基础爆率*N
     * */
    private static Map<Integer, Integer> getGiftConfig(Integer userId) {
        Map<Integer, Integer> nMap = userNMap.get(userId);
        Map<Integer, Integer> giftCoinfig = getGiftCoinfigA();
        Map<Integer, Integer> result = new LinkedHashMap<> ();
        for (Integer price : giftCoinfig.keySet()) {
            result.put(price, giftCoinfig.get(price));
            if (nMap!=null && nMap.containsKey(price)) {
                result.put(price, (int) (giftCoinfig.get(price)*nMap.get(price)));
            }
        }
        return result;
    }

    /**
     * 抽中礼物 跟新N值
     * @Param luck 当次中间礼物
     * */
    private static void updateLuck(Integer userId, Integer luck) {
        Map<Integer, Integer> nMap = userNMap.get(userId);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            for (Integer k : getGiftCoinfigC().keySet()) {
                nMap.put(k, 1);
            }
        }
        for (Integer  price: nMap.keySet()) {
            if (price.equals(luck)) {
                nMap.put(price, 1);
            } else {
                nMap.put(price, nMap.get(price)+1);
            }
        }

        userNMap.put(userId, nMap);
    }

    /**
     * 是否暴击
     * prob 概率 一万次中奖次数
     * */
    static boolean baoji(int prob) {
        int r = new Random().nextInt(10000);
        return r<prob;
    }

    //测试暴击 prob 初始爆率 len 模拟次数
    static double testCrit(int prob, int len) {
        List<Integer> list = new ArrayList<>();
        for (int i=0 ; i<len; i++){
            int times = 0;
            boolean crit = false;
            while (!crit) {
                times++;
                crit = baoji(prob * times);
            }
            list.add(times);
//            System.out.println(times);
        }
//        System.out.println("平均："+list.stream().mapToInt(Integer::intValue).average());
//        System.out.println("最大："+list.stream().mapToInt(Integer::intValue).max());
//        System.out.println("最小："+list.stream().mapToInt(Integer::intValue).min());
        return list.stream().mapToInt(Integer::intValue).average().getAsDouble();

    }

    private static Node getNodeTree(Map<String, Integer> map){
        Node f2 = new Node("f2", new Node(77777), new Node(99999), map);
        Node f1 = new Node("f1", new Node(33440), new Node(52000), map);
        Node e1 = new Node("e1", new Node(13140), new Node(18888), map);
        Node e2 = new Node("e2", f1, f2, map);
        Node d1 = new Node("d1", new Node(6666), new Node(9999), map);
        Node d2 = new Node("d2", e1, e2, map);
        Node c1 = new Node("c1", new Node(1111), new Node(3333), map);
        Node c2 = new Node("c2", d1, d2, map);
        Node b1 = new Node("b1", new Node(188), new Node(521), map);
        Node b2 = new Node("b2", c1, c2, map);
        return new Node("a", b1, b2, map);
    }

    private static Map <String, Integer> getCMap1() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 684);
        map.put("b1", 4934);
        map.put("b2", 846);
        map.put("c1", 1424);
        map.put("c2", 389);
        map.put("d1", 781);
        map.put("d2", 147);
        map.put("e1", 1424);
        map.put("e2", 0);
        map.put("f1", 0);
        map.put("f2", 0);
        return map;
    }
    private static Map <String, Integer> getCMap2() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 503);
        map.put("b1", 4409);
        map.put("b2", 1078);
        map.put("c1", 1424);
        map.put("c2", 1186);
        map.put("d1", 631);
        map.put("d2", 389);
        map.put("e1", 1982);
        map.put("e2", 0);
        map.put("f1", 389);
        map.put("f2", 0);
        return map;
    }
    private static Map <String, Integer> getCMap3() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 425);
        map.put("b1", 4409);
        map.put("b2", 1053);
        map.put("c1", 1424);
        map.put("c2", 1201);
        map.put("d1", 496);
        map.put("d2", 846);
        map.put("e1", 2140);
        map.put("e2", 227);
        map.put("f1", 1078);
        map.put("f2", 0);
        return map;
    }
    private static Map <String, Integer> getCMap4() {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("a", 308);
        map.put("b1", 3553);
        map.put("b2", 1222);
        map.put("c1", 1424);
        map.put("c2", 1483);
        map.put("d1", 291);
        map.put("d2", 1894);
        map.put("e1", 2509);
        map.put("e2", 631);
        map.put("f1", 995);
        map.put("f2", 1424);
        return map;
    }
    static Node node1;
    static Node node2;
    static Node node3;
    static Node node4;

    private static Node getNodeTree(int type){
        if (type == 1) {
            if (node1==null) {
                node1 = getNodeTree(getCMap1());
            }
            return node1;
        } else if (type == 2) {
            if (node2==null) {
                node2 = getNodeTree(getCMap2());
            }
            return node2;
        }  else if (type == 3) {
            if (node3==null) {
                node3 = getNodeTree(getCMap3());
            }
            return node3;
        } else {
            if (node4==null) {
                node4 = getNodeTree(getCMap4());
            }
            return node4;
        }
    }

    private static Map<Integer, Map<String, Integer>> userNMap1 = new HashMap<>();
    private static Map<Integer, Map<String, Integer>> poolNMap = new HashMap<>();

    /**奖池总积分树*/
    static Map<String, Integer> getPoolNMap(int type) {
        Map<String, Integer> nMap = poolNMap.get(type);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            poolNMap.put(type, nMap);
        }
        return nMap;
    }

    /**用户积分树*/
    static Map<String, Integer> getUserNMap(int id) {
        Map<String, Integer> nMap = userNMap1.get(id);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            userNMap1.put(id, nMap);
        }
        return nMap;
    }

    /**
     * 抽中礼物 跟新N值
     * @Param luck 当次中间礼物
     * */
    private static void updateLuck1(Integer userId, String... names) {

        Map<String, Integer> nMap = userNMap1.get(userId);
        if (nMap==null) {
            nMap = new LinkedHashMap<>();
            for (String k : getCMap1().keySet()) {
                nMap.put(k, 1);
            }
        }

        List list = CollectionUtils.arrayToList(names);
        for (String  k: nMap.keySet()) {
            if (list.contains(k)) {
                nMap.put(k, 1);
            } else {
                nMap.put(k, nMap.get(k)+1);
            }
        }

        userNMap1.put(userId, nMap);
    }

    static void testLuck2(Map <String, Integer> map){

        Map<Integer, Integer> out = new HashMap<>();
        for (int j = 0; j < 1; j++) {
            int id = 1;
            for (int i = 0; i < 100000; i++) {
                Map<String, Integer> nMap = getUserNMap(i);
                Map<String, Integer> allMap = getPoolNMap(1);
                int luck = nodeLuck(getNodeTree(map), 1, 1, 2000000, nMap, allMap, 1);

                if (out.containsKey(luck)) {
                    out.put(luck, out.get(luck) + 1);
                } else {
                    out.put(luck, 1);
                }
            }
        }
        printConfig(out);
    }



    /**
     * 抽奖
     * */
    static int nodeLuck(Node node,double poolRate, int min, int max, Map<String, Integer> nMap, Map<String, Integer> allMap, double baseRate) {
        if (node.luck!=0) {
            return node.luck;
        }
        Integer times = nMap.getOrDefault(node.name, 1);
        Integer timesAll = allMap.getOrDefault(node.name, 1);

        int rate = node.getRate(min, max);
        int realRate = 10000;
        if (rate != 10000) {
            realRate = (int) (rate * (Math.max(timesAll, times * 0.7d)) * poolRate * baseRate);
        }
        if (baoji(realRate)) {
            int right = nodeLuck(node.right, poolRate, min, max, nMap, allMap, baseRate);
            if (right>0) {
                allMap.put(node.name, 1);
                nMap.put(node.name, 1);
            }
            return right;
        } else {
            int left = nodeLuck(node.left,poolRate, min, max, nMap, allMap, baseRate);
            if (left>0) {
                nMap.put(node.name, times+1);
                allMap.put(node.name, timesAll+1);
            }
            return left;
        }
    }

    /**
     * 计算奖池配置
     * */
    static int calPercent(Node node, Map<Integer, Integer> map) {
        if (node==null || node.luck!=0) {
            return map.getOrDefault(node.luck, 0);
        } else {
            int right = calPercent(node.right, map);
            int left = calPercent(node.left, map);
            double percent = right+left==0 ? 0 : BigDecimal.valueOf(100d*right/(left+right)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            System.out.println(node.name+" p:"+percent + "  r:"+node.rate + " c:"+getC(percent));
            return right+left;
        }
    }

    /**补礼物， 更新概率树*/
    static boolean patch(Node node, int luck, Map<String, Integer> nMap, Map<String, Integer> allMap) {
        if (node==null) {
            return false;
        }
        if (node.luck!=0) {
            return node.luck==luck;
        }

        if (patch(node.right, luck, nMap, allMap)) {
//            allMap.put(node.name, 1);
            nMap.put(node.name, 1);
            return true;
        }
        return patch(node.left, luck, nMap, allMap);
    }

    /**根据预期概率取C值
     * */
    static int getC(double rate){
        LinkedHashMap<Integer, Double> cConfig = getCConfig();
        for (Map.Entry<Integer, Double> e : cConfig.entrySet()) {
            Object o = e.getValue();
            if (o instanceof BigDecimal) {
                BigDecimal b =  (BigDecimal) o;
                if (b.doubleValue()>rate) {
                    return e.getKey();
                }
            }

        }
        return 0;
    }

    /**
     * 获取C值表
     * key c值, value 预期概率
     * */
    static LinkedHashMap<Integer, Double> getCConfig(){
        try {
            LinkedHashMap<Integer, Double> map = JSON.parseObject(new FileInputStream("c.json"), LinkedHashMap.class);
            if (map==null || map.isEmpty()) {
                map = new LinkedHashMap<>();
                for (int prob = 1; prob <= 10000; prob++) {
                    double crit = testCrit(prob, 10000);
                    System.out.println("概率：" + 100.d / crit + " C:" + prob);
                    map.put(prob, 100.d / crit);
                }
                String json = JSON.toJSONString(map);

                FileWriter fileWriter = new FileWriter("c.json");
                fileWriter.write(json);
                fileWriter.flush();
                fileWriter.close();
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static void main(String[] args) {
        printConfig(getGiftCoinfigD());
        /*System.out.println("--------------");
        testLuck2(getCMap4());
        System.out.println("--------------");
        calPercent(getNodeTree(getCMap4()), getGiftCoinfigD());*/

        /*LuckyUtil32 luckyUtil = new LuckyUtil32();
        Map <Integer, Integer> result = new LinkedHashMap<>();
        List<UserLuck> userLucks = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            UserLuck user = new UserLuck();
            user.id=i;
            while (true) {
                luckyUtil.luckTest(user, result);
                if (user.totalPay>=1000000 || (user.totalIncome*0.85d-user.totalPay>0)) {
//                if (user.totalPay>=1000000) {
                    double rate  = BigDecimal.valueOf(user.totalIncome).divide(BigDecimal.valueOf(user.totalPay), 4, BigDecimal.ROUND_HALF_UP).doubleValue();
                    System.out.println(i+": 投入："+user.totalPay+" 产出:"+user.totalIncome +"  爆率:"+rate+ " 实际盈利："+(int)(user.totalIncome*0.85d-user.totalPay));
                    break;
                }
            }
            userLucks.add(user);
        }
        printConfig(result);

        tjUser(userLucks);

        System.out.println("------------系统爆率------------");
        System.out.println("A 系统投入:"+luckyUtil.poolA.allPay+" 产出:"+luckyUtil.poolA.allIncome+" 总爆率:"+ ((double)luckyUtil.poolA.allIncome/luckyUtil.poolA.allPay)+" 奖池剩余："+luckyUtil.poolA.giftCount+" 剩余爆率:"+((double)luckyUtil.poolA.pool/price/luckyUtil.poolA.giftCount));
        System.out.println("B 系统投入:"+luckyUtil.poolB.allPay+" 产出:"+luckyUtil.poolB.allIncome+" 总爆率:"+ ((double)luckyUtil.poolB.allIncome/luckyUtil.poolB.allPay)+" 奖池剩余："+luckyUtil.poolB.giftCount+" 剩余爆率:"+((double)luckyUtil.poolB.pool/price/luckyUtil.poolB.giftCount));
        System.out.println("C 系统投入:"+luckyUtil.poolC.allPay+" 产出:"+luckyUtil.poolC.allIncome+" 总爆率:"+ ((double)luckyUtil.poolC.allIncome/luckyUtil.poolC.allPay)+" 奖池剩余："+luckyUtil.poolC.giftCount+" 剩余爆率:"+((double)luckyUtil.poolC.pool/price/luckyUtil.poolC.giftCount));
        System.out.println("D 系统投入:"+luckyUtil.poolD.allPay+" 产出:"+luckyUtil.poolD.allIncome+" 总爆率:"+ ((double)luckyUtil.poolD.allIncome/luckyUtil.poolD.allPay)+" 奖池剩余："+luckyUtil.poolD.giftCount+" 剩余爆率:"+((double)luckyUtil.poolD.pool/price/luckyUtil.poolD.giftCount));
        System.out.println("总 系统投入:"+(luckyUtil.poolA.allPay+luckyUtil.poolB.allPay+luckyUtil.poolC.allPay+luckyUtil.poolD.allPay)+
                " 产出:"+(luckyUtil.poolA.allIncome+luckyUtil.poolB.allIncome+luckyUtil.poolC.allIncome+luckyUtil.poolD.allIncome)+
                " 总爆率:"+ ((double)(luckyUtil.poolA.allIncome+luckyUtil.poolB.allIncome+luckyUtil.poolC.allIncome+luckyUtil.poolD.allIncome)/(luckyUtil.poolA.allPay+luckyUtil.poolB.allPay+luckyUtil.poolC.allPay+luckyUtil.poolD.allPay)));
    */}
}

